﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data.Entity;
using System.Data.Metadata.Edm;
using System.Data.Common;
using System.Data.EntityClient;
using StackExchange.Profiling.Data;
using StackExchange.Profiling;
using System.Reflection.Emit;

namespace DataBase.Daihoctuonglai
{
    public static class DbContextUtils
    {
        private const BindingFlags PrivateInstance = BindingFlags.NonPublic | BindingFlags.Instance;

        public static T CreateDbContext<T>() where T : DbContext
        {
            return CreateDbContext<T>(GetProfiledConnection<T>());
        }

        public static T CreateDbContext<T>(this DbConnection connection) where T : DbContext
        {
            var workspace = new MetadataWorkspace(new[] { "res://*/" }, new[] { typeof(T).Assembly });
            var factory = DbProviderServices.GetProviderFactory(connection);

            var itemCollection = workspace.GetItemCollection(DataSpace.SSpace);
            var providerFactoryField = itemCollection.GetType().GetField("_providerFactory", PrivateInstance);
            if (providerFactoryField != null) providerFactoryField.SetValue(itemCollection, factory);

            var ec = new EntityConnection(workspace, connection);

            return CtorCache<T, DbConnection>.Ctor(ec);
        }

        public static DbConnection GetProfiledConnection<T>() where T : DbContext
        {
            var dbConnection = ObjectContextUtils.GetStoreConnection("name=" + typeof(T).Name);
            return new EFProfiledDbConnection(dbConnection, MiniProfiler.Current);
        }

        internal static class CtorCache<TType, TArg> where TType : class
        {
            public static readonly Func<TArg, TType> Ctor;
            static CtorCache()
            {
                var argTypes = new[] { typeof(TArg) };
                var ctor = typeof(TType).GetConstructor(argTypes);
                if (ctor == null)
                {
                    Ctor = x => { throw new InvalidOperationException("No suitable constructor defined"); };
                }
                else
                {
                    var dm = new DynamicMethod("ctor", typeof(TType), argTypes);
                    var il = dm.GetILGenerator();
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Newobj, ctor);
                    il.Emit(OpCodes.Ret);
                    Ctor = (Func<TArg, TType>)dm.CreateDelegate(typeof(Func<TArg, TType>));
                }
            }
        }
    }
}
